#ifndef _SCENENODE_H
#define _SCENENODE_H

#include <cassert>
#include <cstdlib>
#include <vector>
#include <set>

#include <tr1/memory>

#include <gtkmm.h>
#include <gtkglmm.h>

#include "joeserialize.h"
#include "macros.h"
#include "reseatable_reference.h"
#include "utils.h"
#include "mathvector.h"

class SceneNodeField
{
	public:
		SceneNodeField() : guiMin(0), guiMax(1) {}
		
		const std::string & getValue() const {return value;}
		void setValue(const std::string & newValue) {value = newValue;}
		const std::string & getType() const {return type;}
		void setGuiSliderRange(float min, float max)
		{
			guiMin = min;
			guiMax = max;
		}
		std::pair <float, float> getGuiSliderRange() const
		{
			return std::make_pair(guiMin, guiMax);
		}
		
		bool Serialize(joeserialize::Serializer & s)
		{
			_SERIALIZE_(s,type);
			_SERIALIZE_(s,value);
			_SERIALIZE_(s,guiMin);
			_SERIALIZE_(s,guiMax);
			return true;
		}
		
		bool operator==(const SceneNodeField & other) const
		{
			return (type == other.type && value == other.value &&
				guiMin == other.guiMin && guiMax == other.guiMax);
		}
		
		void upgradeFrom(const SceneNodeField & other)
		{
			value = other.value;
			guiMin = other.guiMin;
			guiMax = other.guiMax;
		}
	
	private:
		std::string type;
		std::string value;
		float guiMin;
		float guiMax;
};

struct SharedFloatVector
{
	SharedFloatVector() {}
	SharedFloatVector(const SharedFloatVector & other) {pointer.reset();}
	const SharedFloatVector & operator=(const SharedFloatVector & other) {pointer.reset();return *this;}
	std::tr1::shared_ptr <std::vector <float> > pointer;
};

class SceneManager;

class SceneNode
{
	friend class SceneManager;
	public:
		SceneNode() : name("unnamed"),icon("gtk-file"),resettable(false) {}
		SceneNode(const std::string & newname) : name(newname),icon("gtk-file"),resettable(false) {}
		SceneNode(const SceneNode & other, const std::string & newname)
		{
			*this = other;
			name = newname;
		}
		const std::string & getIcon() const {return icon;}
		const std::string & getName() const {return name;}
		const std::string & getType() const {return type;}
		void setName(const std::string & newName) {name = newName;}
		const std::string & getCategory() const {return category;}
		const std::string & getDescription() const {return description;}
		reseatable_reference <SceneNode> addNode(const SceneNode & newNode, const SceneNode * beforeNodeIfPossible = NULL);
		bool deleteNode(const SceneNode & node);
		bool canHaveChild(const std::string & childType, const std::string & childCategory) const;
		bool canHaveChild(const SceneNode & other) const {return canHaveChild(other.type, other.category);}
		bool canHaveChildren() const {return !allowedChildren.empty();}
		SceneNode * getParent(const SceneNode & node);
		const SceneNode * getParent(const SceneNode & node) const;
		const SceneNode * getNextSibling(const SceneNode & node) const;
		
		// get a field's value and store it in the provided variable
		// does nothing to the provided variable if the field does not exist
		// returns true if the field exists and isn't blank, false otherwise
		template <typename T>
		bool getField(const std::string & fieldname, T & value) const
		{
			fields_type::const_iterator i = fields.find(fieldname);
			if (i != fields.end() && !i->second.getValue().empty())
			{
				std::stringstream s(i->second.getValue());
				s >> value;
				return true;
			}
			else
				return false;
		}
		
		bool getField(const std::string & fieldname, std::string & value) const
		{
			fields_type::const_iterator i = fields.find(fieldname);
			if (i != fields.end() && !i->second.getValue().empty())
			{
				value = i->second.getValue();
				return true;
			}
			else
				return false;
		}
		
		// set a field's value to the provided value
		// does nothing if the field does not exist
		template <typename T>
		void setField(const std::string & fieldname, T value)
		{
			fields_type::iterator i = fields.find(fieldname);
			if (i != fields.end())
			{
				std::stringstream s;
				s << value;
				i->second.setValue(s.str());
			}
		}
		
		// Recursively get a list of all descendants.
		// If typeFilter is set, then only return children that
		// match the typeFilter type, but still recurse into them.
		// If typeMask is set, then only recurse into children of the
		// same type as the typeMask node if they match the typeMask node.
		// If noRecurseIntoType is set, don't recurse into nodes of that type.
		void getChildren(std::vector <const SceneNode*> & output, 
						 bool recurse = true, 
						 const std::string & typeFilter = "",
						 const SceneNode * typeMask = NULL,
						 const std::string & noRecurseIntoType = "",
						 std::vector <std::string> * paths = NULL,
						 std::vector <std::string> curPath = std::vector <std::string>()) const;
		
		bool isEditable() const {return !fields.empty();}
		
		typedef std::map <std::string, SceneNodeField> fields_type;
		fields_type & getFields() {return fields;}
		const fields_type & getFields() const {return fields;}
		
		void copyWithoutChildren(const SceneNode & node)
		{
			*this = node;
			children.clear();
		}
		
		bool Serialize(joeserialize::Serializer & s)
		{
			_SERIALIZE_(s,name);
			_SERIALIZE_(s,type);
			_SERIALIZE_(s,category);
			_SERIALIZE_(s,allowedChildren);
			_SERIALIZE_(s,icon);
			_SERIALIZE_(s,children);
			_SERIALIZE_(s,description);
			_SERIALIZE_(s,fields);
			_SERIALIZE_(s,resettable);
			return true;
		}
		
		std::tr1::shared_ptr <std::vector <float> > getFloatVars();
		void updateFloatVars();
		void setFloatVars(const double * array16);
		
		// recurse through all children upgrading them to conform to the latest
		// node type prototypes
		void upgrade(const std::map <std::string, SceneNode> & nodeTypes);
		
		void convertPaths(const std::string & absoluteReference, bool absoluteToRelative);
		
		// recurse through descendents looking for nodes that may reference this node and updating their names
		void handleNodeRename(const SceneNode & node, const std::string & oldname);
		
		bool isResettable() const {return resettable && children.empty();}
		
	private:
		std::string name;
		std::string type;
		std::string category; // used to place the "add node" item in the appropriate pop-up submenu
		std::set <std::string> allowedChildren; // strings that can be * for anything, categoryname for everything in a category, or typename for specific types
		std::string icon;
		std::list <SceneNode> children;
		std::string description;
		fields_type fields;
		bool resettable;
		
		// This is used for "variable" types so we can cache off float formatted variables
		// for the renderer to use.
		// The renderer will reference this so that it won't go away until the renderer is ready for it to.
		// The SceneNode will allocate the shared_ptr when it is first asked for.
		// Copying floatVars will cause the pointer to be invalidated and regenerated.
		SharedFloatVector floatVars;
};

#endif
